#include "GameApplication.h"

#include <SFML/Window/VideoMode.hpp>
#include <SFML/Window/Event.hpp>

#include "MainMenu.h"

using namespace mega;
using namespace sf;


const char * GameApplication::m_windowTitle = "Mandarine";


GameApplication::GameApplication()
	: m_window(VideoMode::getDesktopMode(), GameApplication::m_windowTitle, Style::Fullscreen)
	, m_currentViewIndex(0)
{
	// start from main menu
	m_views.push_back(new MainMenu);
}


GameApplication::~GameApplication()
{
	for (auto iter = std::begin(m_views); iter != std::end(m_views); ++iter)
	{
		delete *iter;
		*iter = nullptr;
	}
}


GameApplication & GameApplication::instance()
{
	static GameApplication instance;

	return instance;
}


sf::RenderWindow & GameApplication::window()
{
	return m_window;
}


void GameApplication::pushView(GameView * _view)
{
	m_views.push_back(_view);
	++m_currentViewIndex;
}


void GameApplication::popView()
{
	// close window in case there is only one view
	if (m_currentViewIndex == 0)
	{
		m_window.close();
		return;
	}
	else
	{
		--m_currentViewIndex;
	}

	// remove following views if present
	if (m_currentViewIndex > m_views.size() - 1)
	{
		for (size_t i = 0; i < m_currentViewIndex - m_views.size() - 1; ++i)
		{
			delete m_views.back();
			m_views.back() = nullptr;
			m_views.pop_back();
		}
	}
}


int GameApplication::run()
{
	while (m_window.isOpen())
	{
		processEvents();

		m_window.clear();

		// TODO: provide rendering for lower-level views if needed
		m_views.at(m_currentViewIndex)->render(m_window);

		m_window.display();
	}

	return EXIT_SUCCESS;
}


void GameApplication::processEvents()
{
	Event event;

	while (m_window.pollEvent(event))
	{
		switch (event.type)
		{
			case Event::KeyPressed:
				onKeyPressed(event.key.code);
				break;

			case Event::KeyReleased:
				onKeyReleased(event.key.code);
				break;

			case Event::MouseButtonPressed:
				onMousePressed(event.mouseButton.button, m_window.mapPixelToCoords(Mouse::getPosition()));
				break;

			case Event::MouseButtonReleased:
				onMouseReleased(event.mouseButton.button, m_window.mapPixelToCoords(Mouse::getPosition()));
				break;
		}
	}
}


/*
	TODO: think of providing some conditions of handling events by certain views and not handling by anothers

     the loops below provide handling event
     only by the first view from the top of stack which can handle it.
*/
bool GameApplication::onKeyPressed(const Keyboard::Key & _key)
{
	// by views handling
	// this wild loop breaks if there was a view  which handled event or no one view in the game did that
	for (size_t indent = 0; (indent <= m_currentViewIndex) && !m_views.at(m_currentViewIndex - indent)->onKeyPressed(_key); ++indent);

	return true;
}


bool GameApplication::onKeyReleased(const Keyboard::Key & _key)
{
	// by game handling
	if (Keyboard::Escape == _key)
	{
		if (m_views.size() == 1)
			m_window.close();
		else
			// another one memory leak
			m_views.pop_back();
	}
	else if (Keyboard::Q == _key)
	{
		m_window.close();
	}

	// by views handling
	for (size_t indent = 0; (indent <= m_currentViewIndex) && !m_views.at(m_currentViewIndex - indent)->onKeyReleased(_key); ++indent);

	return true;
}


bool GameApplication::onMousePressed(const Mouse::Button & _button, const Position & _pos)
{
	// by views handling
	for (size_t indent = 0; (indent <= m_currentViewIndex) && !m_views.at(m_currentViewIndex - indent)->onMousePressed(_button, _pos); ++indent);

	return true;
}


bool GameApplication::onMouseReleased(const Mouse::Button & _button, const Position & _pos)
{
	// by views handling
	for (size_t indent = 0; (indent <= m_currentViewIndex) && !m_views.at(m_currentViewIndex - indent)->onMouseReleased(_button, _pos); ++indent);

	return true;
}

mega::GameApplication & mega::gApp()
{
	return GameApplication::instance();
}
